
	/***********************************************************************************************

		@API
		Module: DataSource

		Api:
			mod		type						(id, connector)
			mod		define					(id, options*)

			ds		connect					(id, options*)
			v			execute					(id, command, onExecute*)

		Module Options:
			datasource { type }

		Events:
			beforeType,  beforeDefine,	beforeConnect,	beforeExecute
			type,        define,				connect,				execute

		Note:
		 	predefined datasource types:
		 		net(d), db, memory

      column data types:
	      number:   i,i+,i++,n,n+,n++
	      string:   s,s+
	      bool:     b
	      date:     d
	      virtual:  v
	      untyped:  any
	      blob:     blob
	      xml:      xml
	      json:     json

	************************************************************************************************/

	//-------------------------------------------------------------------------------
	jsl.validator("ds.type", function(v, rule, id, error) {
		if(v == null)					throw error("NullValue" 	, [ id ])
		if(!jsl.isString(v))	throw error("BadType" 		, [ id, "string" ])
		if(!v.length)					throw error("EmptyString"	, [ id ])
		if(!jsl.DataSource.__cs[v.toLowerCase()])	throw error("e.ds.TypeNotDefined", [ v ])
	})

	//-------------------------------------------------------------------------------
	jsl.DataSource = (function() {
		try {
			var mod

			mod 			= new jsl.Module("jsl.DataSource")
			mod.__cs	= {}
			mod.__ds	= {}

			//-------------------------------------------------------------------------------------------
			mod.events("beforeType",  "beforeDefine",	"beforeConnect",	"beforeExecute")
			mod.events("type",        "define",				"connect",				"execute")

      //-------------------------------------------------------------------------------------------
			mod.option("datasource")
			mod.option("datasource.type", {
				dvalue: 		"net",
				validator:	"ds.type",
				setter:			function(v, oldv, id) { return v.toLowerCase() }
			})

			return mod
		}
		catch(e) {
			jsl.throwError(e, "jsl.DataSource")
		}
	})()
	//-------------------------------------------------------------------------------
	jsl.DataSource.type = function(id, connector) {
		try {
			var e//!
			if(arguments.length !== 2)	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")
			if(id.indexOf(".") !== -1)	throw jsl.ParamError("BadCharacter:id:.")
			jsl.validate(connector, "o", "connector")
			if(!this.Connector.isImplementedBy(connector)) throw jsl.ParamError("InterfaceNotImplemented:connector:DataSource.Connector")//!

			//the type can be redefined
			e = new jsl.Event
			e.id = id + ""
			e.connector  = connector

			this.throwEvent("beforeType", e)
			this.__cs[id.toLowerCase()] = jsl.Object.clone(connector)
      this.throwEvent("type", e)

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	/*
    options:
      location, readonly
	*/
	//-------------------------------------------------------------------------------
	jsl.DataSource.define = function(id, options) {
		try {
			var tks, type, nid, ds, e//!
			if(arguments.length !== 2)	throw jsl.ParamError()
      jsl.validate(id, "s+", "id")
			jsl.validate(options, "t(s+,o)", "options")//!

      tks   = id.toLowerCase().split(".")
      type  = tks.length >= 2 ? tks[0] : this.get("datasource.type")
      nid   = tks.length >= 2 ? tks[1] : tks[0]

      //!
      if(tks.length > 2)    throw jsl.ParamError("BadSyntax:      id")
      if(!type)             throw jsl.ParamError("EmptyString:    id(type)")
      if(!nid)              throw jsl.ParamError("EmptyString:    id(datasource)")
      if(!this.__cs[type])  throw jsl.ParamError("NotDefined:     id(type)")
      if(this.__ds[nid])    throw jsl.ParamError("AlreadyDefined: id(id)")//!

			if(jsl.isString(options))  ds = { location: options }
			else                       ds = jsl.Object.clone(options)

      //!
      if(ds.location  == null) throw jsl.ParamError("Required:options.location")
      if(ds.readonly  != null) jsl.validate(ds.readonly, "b",  "options.readonly")//!

      //a location can be of any type, it is converted to string ex: jsl.Url
      ds.id         = nid
      ds.type       = type
      ds.readonly   = !!+ds.readonly
      ds.location   = ds.location + ""

      e = new jsl.Event
      e.datasource = ds

			this.throwEvent("beforeDefine", e)
			this.__cs[type].define(ds, options)
			this.__ds[nid] = ds
			this.throwEvent("define", e)
			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	/*
    options:
      readonly, metadata, onConnect, onCommit, onRefresh
	*/
	//-------------------------------------------------------------------------------
	jsl.DataSource.connect = function(id, options) {
		try {
			var os, tks, ds, tb, e, cn, md, f, Ds, args//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
      jsl.validate(id, "s+", "id")
			if(arguments.length === 2 && options == null)	throw jsl.ParamError("NullValue:options")//!

      tks = id.toLowerCase().split(".")
      if(tks.length > 2) tks[1] = tks.slice(1).join(".")

      ds  = this.__ds[tks[0]]
      tb  = tks[1]
      os  = {}

      //!
      if(!tks[0])                 throw jsl.ParamError("EmptyString:  id(datasource)")
      if(!ds)                     throw jsl.ParamError("NotDefined:   id(datasource)")
      if(tks.length >= 2 && !tb)  throw jsl.ParamError("EmptyString:  id(table)")//!

			if(jsl.isArray(options))		     os.metadata  = options
      else if(jsl.isFunction(options)) os.onConnect = options
      else if(jsl.isObject(options))   os           = jsl.Object.clone(options)
      else if(arguments.length === 2)  os.option    = options

      if(os.readonly == null) os.readonly = ds.readonly

			//!
			if(os.onConnect != null) jsl.validate(os.onConnect,  "f",         "options.onConnect")
      if(os.onCommit  != null) jsl.validate(os.onCommit,   "f",         "options.onCommit")
      if(os.onRefresh != null) jsl.validate(os.onRefresh,  "f",         "options.onRefresh")
      if((os.onCommit || os.onRefresh) && !os.onConnect)  throw jsl.ParamError("Expected:options.onConnect")
      jsl.validate(os.readonly,   "b", "options.readonly")
			if(os.readonly === false && ds.readonly) throw jsl.ParamError("OperationDenied:options.readonly")//!

      if(os.metadata != null) {

        md = os.metadata = this.__md(os.metadata, "options.metadata")

        for(var a = 0, l = md.length; a < l; a++)
          if(md[a].type === "v")  md.virtuals = true
          else                    md.reals    = true
      }

      os.readonly   = !!+os.readonly
      e             = new jsl.Event
      e.datasource  = ds
      e.table       = tb
      e.options     = os

      this.throwEvent("beforeConnect", e)
      f   = os.onConnect
      cn  = jsl.Object.clone(this.__cs[ds.type])

      //1-async
      if(f) {

      	Ds   = this
      	args = arguments
      	os.onReady = function(ds) {
          ds.__cn       = cn
          ds.__cn.ASY   = true
          ds.__ods      = ds.__ds.slice()
          ds.onCommit   = os.onCommit
          ds.onRefresh  = os.onRefresh
          e.dataset     = ds

          try       { f.call(Ds, e)                                                             }
          catch(er) { throw jsl.ParamError("ScriptError:options.onConnect", er).trace(Ds, args) }
          Ds.throwEvent("connect", e)
      	}

        cn.connect(ds, tb, os)
      	ds = null
      }
      //2-sync
      else {
        ds = cn.connect(ds, tb, os)
        ds.__cn   = cn
        ds.__ods  = ds.__ds.slice()
        e.dataset = ds
        this.throwEvent("connect", e)
      }

			return ds
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.DataSource.execute = function(id, command, onExecute) {
		try {
			var tks, ds, cn, e, v, Ds, args//!
			if(arguments.length < 2 || arguments.length > 3)   throw jsl.ParamError()
      jsl.validate(id, "s+", "id")
      if(command == null)          throw jsl.ParamError("NullValue:command")
      if(arguments.length === 3)  jsl.validate(onExecute, "f", "onExecute")//!

			tks = id.toLowerCase().split(".")
      ds  = this.__ds[tks[0]]

      //!
      if(tks.length > 1)  throw jsl.ParamError("Expected:   id=datasource")
      if(!ds)             throw jsl.ParamError("NotDefined: id(datasource)")//!

			cn 	= this.__cs[ds.type]

			e	= new jsl.Event
			e.datasource 	= ds
			e.command			= command

      this.throwEvent("beforeExecute", e)

			//1-async
			if(onExecute) {

				Ds    = this
				args  = arguments

        cn.execute(ds, command, function(v) {

        	e.result = v
          try       { onExecute.call(Ds, e)                                                     }
          catch(er) { throw jsl.ParamError("ScriptError:options.onExecute", er).trace(Ds, args) }
          this.throwEvent("execute", e)
        })

				v = null
			}
			//2-sync
			else {
        v = e.result = cn.execute(ds, command)
        this.throwEvent("execute", e)
			}

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//private
	//-------------------------------------------------------------------------------
	jsl.DataSource.__toNative = function(v, t) {
		try {
			var D, xo, jo, nv

			if(v == null)              nv = null
      else if(jsl.isString(v))   nv = v + ""
			else if(jsl.isBoolean(v))  nv = !!+v
			else if(jsl.isNumber(v))	 nv = +v
			else if(jsl.isDate(v))		 nv = +v
      else {

        D   = jsl.require("data")
        xo  = D.__XML_OPTS
        jo  = D.__JSN_OPTS

        if(t === "any")               nv = D.serialize(v)
			  else if(v instanceof D.Blob)  nv = v.toString(16)
		  	else if(v instanceof D.XNode) nv = D.toXml(v, xo)
        else                          nv = D.toJson(v, jo)
      }

			return nv
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.DataSource.__fromNative = function(nv, t, cx) {
		var v

		v = nv
		switch(t) {
			//!1-checks (native data only primitive)
			//-------------------------------------------------------------------------------
			case "i":
				if(!jsl.isInteger(nv))  					      throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "n":
				if(isNaN(nv))	         						      throw jsl.DataError("e.ds.BadValue", [ cx ])
        if(!isFinite(nv))                       throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "i+":
				if(!jsl.isInteger(nv) || nv < 0)		    throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "n+":
				if(isNaN(nv) || nv < 0)			            throw jsl.DataError("e.ds.BadValue", [ cx ])
        if(!isFinite(nv))                       throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "i++":
				if(!jsl.isInteger(nv) || nv <= 0)       throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "n++":
				if(isNaN(nv) || nv <= 0)		            throw jsl.DataError("e.ds.BadValue", [ cx ])
        if(!isFinite(nv))                       throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "s":
				if(typeof nv !== "string")		  	      throw jsl.DataError("e.ds.BadValue", [ cx ])
				break

			case "s+":
				if(typeof nv !== "string" || !nv.length)throw jsl.DataError("e.ds.BadValue", [ cx ])
				break//!

      //2-checks and transformations
			case "b"://!
				if(nv !== true && nv !== false &&
				   nv !== 0    && nv !== 1)			       throw jsl.DataError("e.ds.BadValue", [ cx ])//!

				v = !!nv
				break

			//-------------------------------------------------------------------------------
			case "d"://!
				if(!jsl.isInteger(nv) || nv <= 0)		   throw jsl.DataError("e.ds.BadValue", [ cx ])//!
				v = new Date(nv)
				break

			case "blob"://!
				if(nv !== null && typeof nv !== "string") throw jsl.DataError("e.ds.BadValue", [ cx ])//!
				if(nv !== null) v = new ((jsl.Data || jsl.require("data")).Blob)(nv)
				break

			case "json"://!
				if(nv !== null && typeof nv !== "string") throw jsl.DataError("e.ds.BadValue", [ cx ])//!
				if(nv !== null) v = (jsl.Data || jsl.require("data")).fromJson(nv, jsl.Data.__JSN_OPTS)
				break

			case "xml"://!
				if(nv !== null && typeof nv !== "string") throw jsl.DataError("e.ds.BadValue", [ cx ])//!
				if(nv !== null) v = (jsl.Data || jsl.require("data")).fromXml(nv, jsl.Data.__XML_OPTS)
				break

			case "any":
				if(typeof nv === "string" && nv.slice(0, 5) === "JSL~S") v = (jsl.Data || jsl.require("data")).deserialize(nv)
		}
		return v
	}
	//-------------------------------------------------------------------------------
	jsl.DataSource.__md = function(metadata, cx) {
		try {
			var md, mda, id, ky, tp, vl, c, icx, D, hasK

			md = null
			if(metadata) {

				icx = cx || "metadata"//!
				jsl.validate(metadata, "a", icx)//!

				md  = []
				D   = jsl.Data

				for(var a = 0, l = metadata.length; a < l; a++) {

					cx = icx + "[" + a + "]"//!
					jsl.validate(metadata[a], "t(o,s+)", cx)//!

					mda	= metadata[a]

					if(jsl.isString(mda)) {
						id	= mda
						ky	= false
						tp 	= "s"
						vl	= ""
					}
					else {
						id	= mda.id
						ky	= mda.key
						tp 	= mda.type
						vl	= mda.value
					}

					//!
					jsl.validate(id, "s+", cx + "(id)")
					ky != null && jsl.validate(ky, "b", cx + "(key)")//!
          cx = icx + "[id=" + id + "]"

					//!
					if(tp != null) jsl.validate(tp, "sr/^((i|n)\\+{0,2}|s|s\\+|b|d|v|any|blob|xml|json)$", cx + "(type)")//!
          if(!tp) tp = "s"

					c = tp.charAt(0)

					//1.1-number
					if(c === "i" || c === "n") {//!
						mda.hasOwnProperty("value") && jsl.validate(vl, tp, cx + "(value)")//!

						if(vl != null)									vl = +vl
						else if(tp.slice(-2) === "++")	vl = 1
						else														vl = 0
					}

					//1.2-string
					else if(c === "s") {//!
						mda.hasOwnProperty("value") && jsl.validate(vl, tp, cx + "(value)")//!

						if(vl != null)			vl = vl + ""
						else if(tp === "s")	vl = ""
						else								vl = " "
					}

					//1.3-boolean
					else if(tp === "b") {//!
						mda.hasOwnProperty("value") && jsl.validate(vl, "b", cx + "(value)")//!
						vl = !!+vl
					}

					//1.4-date
					else if(tp === "d") {//!
						mda.hasOwnProperty("value") && jsl.validate(vl, "d", cx + "(value)")//!
						vl = vl || new Date
					}

					//1.5-virtual
					else if(tp === "v") {//!
						if(ky)   throw jsl.ParamError("e.ds.InvalidKey:" + cx)
						jsl.validate(vl, "f", cx + "(value)")//!
					}

					//1.6-blob
					else if(tp === "blob") {//!
						if(ky)	throw jsl.ParamError("e.ds.InvalidKey:" + cx)
						if(vl != null && !(D || jsl.require("data")).Blob.isClassOf(vl))
						 throw jsl.ParamError("BadType:" + cx + "(value):jsl.Data.Blob")//!

						vl = vl || null
					}

					//1.7-xml
					else if(tp == "xml") {//!
						if(ky)	throw jsl.ParamError("e.ds.InvalidKey:" + cx)
						if(vl != null && !(D || jsl.require("data")).XNode.isClassOf(vl))
						 throw jsl.ParamError("BadType:" + cx + "(value):jsl.Data.XNode")//!

						vl = vl || null
					}

					//1.8-json
					else if(tp == "json") {//!
						if(ky) throw jsl.ParamError("e.ds.InvalidKey:" + cx)
						vl != null && jsl.validate(vl, "o", cx + "(value)")//!
						vl = vl || null
					}

					//1.9-any
					else {//!
						if(ky) throw jsl.ParamError("e.ds.InvalidKey:" + cx)//!
						if(!mda.hasOwnProperty("value")) vl = null
					}

					md[a] = {
						id: 		id + "",
						type:		tp + "",
            key:    !!+ky,
						value: 	vl
					}

					if(md[a].key) hasK = true
					md["." + id] = md[a]
				}
			}

			md.hasK = !!hasK
			return md
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
